package com.moxie.ocr.ocr.card;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.widget.TextView;

import com.moxie.ocr.ocr.utils.MXCameraProxy;
import com.moxie.ocr.ocr.utils.MXImageUtils;
import com.moxie.ocr.ocr.utils.MXLog;
import com.moxie.ocr.ocr.utils.MXSensorAccelerometerController;
import com.moxie.ocr.ocr.utils.PreviewSaver;
import com.moxie.ocr.ocr.utils.Util;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


@SuppressWarnings("deprecation")
public abstract class CardScanner implements Camera.PreviewCallback, SurfaceHolder.Callback, MXSensorAccelerometerController.CameraFocusListener {

    private static final String TAG = CardScanner.class.getSimpleName();

    private static final int KEY_MESSAGE_HANDLER_AUTO_FOCUS = 100;

    /**
     * 上下文对象
     */
    protected Context mContext;

    protected TextView mTvDebug;

    /**
     * OCR扫描回调
     */
    protected MXCardScanListener mCardScanListener;

    /**
     * 当前activity的旋转角度
     */
    protected int mRotation;

    private int mFrameOrientation;

    /**
     * 相机操作类
     */
    private MXCameraProxy mMXCameraProxy;

    /**
     * 扫描区域大小
     */
    private Rect mCardScanRect;

    /**
     * 扩边后的扫描区域大小
     */
    private Rect mClipRect;

    /**
     * 预览返回的data
     */
    private byte[][] mPreviewBuffer;

    /**
     * 当前预览帧
     */
    private byte[] mCurPreviewBuffer;

    /**
     * 当前圆方向是否是垂直
     */
    private boolean mCardOrientationVertical = false;

    /**
     * OCR识别类Recognizer
     */
    protected CardRecognizer mCardRecognizer;

    /**
     * 线程池内线程数量
     */
    protected final int mRecognizerNumber = 1;

    /**
     * 线程池
     */
    private ExecutorService mExecutor;

    /**
     * 用于保存当前预览的数据
     */
    private PreviewSaver mPreviewSaver;

    /**
     * 是否需要卡片在框内才可以识别，默认true
     */
    private boolean mIsInFrame = true;

    /**
     * 开始扫描的时间
     */
    private long mStartDetectTime = 0;

    /**
     * 相机是否已经释放
     */
    private boolean mIsCameraRelease = false;

    /**
     * 扫描超时时间，单位秒
     */
    private int mScanTimeOut;

    /**
     * 旋转角度
     */
    private int mRotateDegree;


    private boolean mIsFocusSuccess;
    private MXSensorAccelerometerController mAccelerometerSensor;

    private boolean mIsStartAutoFocus;

    private Handler mMainHandler;

    private long mLastFocusTime;

    private boolean mIsNeedAutoFocus = true;

    private boolean mIsNeedAutoRecognize = true;

    private Camera mCamera = null;

    protected CardScanner(Context context, int currentFrameOrientation, boolean cardOrientationVertical) {
        mContext = context;
        mFrameOrientation = currentFrameOrientation;
        mRotateDegree = (90 * mFrameOrientation) % 360;
        this.mCardOrientationVertical = cardOrientationVertical;
        mMainHandler = new Handler();
    }

    public void init() {
        try {
            initCameraUtils();
            mCardRecognizer = initRecognizer(mContext);
        } catch (RecognizerInitFailException e) {
            e.printStackTrace();
            if (mCardScanListener != null) {
                mCardScanListener.initFail();
            }
        }
    }

    public void setIsNeedAutoRecognize(boolean isNeedAutoRecognize) {
        this.mIsNeedAutoRecognize = isNeedAutoRecognize;
    }

    /**
     * 初始化相机操作相关类
     */
    private void initCameraUtils() {
        mMXCameraProxy = new MXCameraProxy(mContext);
        mMXCameraProxy.setFrameOrientation(mFrameOrientation);
        //如果不支持加速度传感器，则优先设置连续对焦，否则设置自动对焦
        initAccelerometerSensor();
        mMXCameraProxy.setIsContinuousFocusFirst(!isSupportAccelerometerSensor());
    }

    private void initAccelerometerSensor() {
        if (mIsStartAutoFocus) {
            mIsFocusSuccess = false;
            mAccelerometerSensor = MXSensorAccelerometerController.getInstance(mContext);
            mAccelerometerSensor.setCameraFocusListener(this);
        } else {
            mIsFocusSuccess = true;
        }
    }

    private boolean isSupportAccelerometerSensor() {
        return mAccelerometerSensor != null && mAccelerometerSensor.isSupportAccelerometerSensor();
    }

    private void startAccelerometerSensor() {
        if (mAccelerometerSensor != null) {
            mAccelerometerSensor.onStart();
        }
    }

    private void destroyAccelerometerSensor() {
        if (mAccelerometerSensor != null) {
            mAccelerometerSensor.onStop();
        }
    }

    /**
     * 创建线程池
     */
    private void createExecutor() {
        if (mExecutor == null) {
            mExecutor = Executors.newFixedThreadPool(mRecognizerNumber);
        }
    }

    /**
     * 关闭线程池
     */
    private void destroyExecutor() {
        if (mExecutor != null) {
            mExecutor.isShutdown();
            mExecutor = null;
        }
    }

    /**
     * 开启扫描准备
     */
    void prepareScanner() {
        mMXCameraProxy.openCamera();
    }

    /**
     * 重新开始扫描
     *
     * @param holder
     * @return
     */
    boolean resumeScanning(SurfaceHolder holder) {
        boolean isStart = true;
        if (mMXCameraProxy.getCamera() == null) {
            prepareScanner();
        }

        if (mMXCameraProxy.hasCameraPermission(mMXCameraProxy.getCamera())) {
            createExecutor();
            createPreviewBuffer();

            if (holder != null) {
                holder.addCallback(this);
                holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
                mMXCameraProxy.setPreviewCallbackWithBuffer(this);
                startPreview(holder);
            }
        } else {
            isStart = false;
        }
        mStartDetectTime = System.currentTimeMillis();
        return isStart;
    }

    /**
     * 重置扫描超时的初始时间
     */
    public void resetStartDetectTime() {
        mStartDetectTime = System.currentTimeMillis();
    }

    /**
     * 暂停扫描
     */
    public void pauseScanning() {
        if (!mIsCameraRelease) {
            mIsCameraRelease = true;
            mMXCameraProxy.releaseCamera();
            mPreviewBuffer = null;
            destroyExecutor();
            destroyAccelerometerSensor();
        }
    }

    public void setCameraRelease(boolean isCameraRelease) {
        mIsCameraRelease = isCameraRelease;
    }

    /**
     * 停止扫描
     */
    public void endScanning() {
//        pauseScanning();
        CardRecognizer cardRecognizer = getCardRecognizer();
        if (cardRecognizer != null) {
            cardRecognizer.destroyRecognizer();
        }
    }

    /**
     * 创建预览buffer
     */
    private void createPreviewBuffer() {
        if (mPreviewBuffer == null) {
            int previewFormat = ImageFormat.NV21; // the default.

            Camera camera = mMXCameraProxy.getCamera();
            if (camera != null) {

                Camera.Parameters parameters = camera.getParameters();
                previewFormat = parameters.getPreviewFormat();

                int bytesPerPixel = ImageFormat.getBitsPerPixel(previewFormat) / 8;

                int bufferSize = mMXCameraProxy.getPreviewWidth() * mMXCameraProxy.getPreviewHeight() * bytesPerPixel * 3 / 2;

                mPreviewBuffer = new byte[mRecognizerNumber][bufferSize];
                mCurPreviewBuffer = new byte[bufferSize];
                for (int i = 0; i < mRecognizerNumber; i++) {
                    mMXCameraProxy.addCallbackBuffer(mPreviewBuffer[i]);
                }
            }
        }
    }

    /**
     * 开启预览
     *
     * @param holder
     */
    private void startPreview(SurfaceHolder holder) {
        if (holder != null) {
            mMXCameraProxy.setPreviewDisplay(holder);
            mMXCameraProxy.startPreview();
            startAccelerometerSensor();
            mIsNeedAutoFocus = true;
            mLastFocusTime = 0;
            initAccelerometerSensor();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (mMXCameraProxy.getCamera() != null) {
            startPreview(holder);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (mMXCameraProxy.getCamera() != null) {
            startPreview(holder);
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mMXCameraProxy != null) {
            mMXCameraProxy.stopPreview();
        }
    }

    /**
     * 获取OCR识别类
     *
     * @return
     */
    protected CardRecognizer getCardRecognizer() {
        return mCardRecognizer;
    }

    @Override
    public void onPreviewFrame(final byte[] data, final Camera camera) {
        if (data == null) {
            return;
        }

        if (mCamera == null) {
            mCamera = camera;
        }

        synchronized (this) {
            System.arraycopy(data, 0, mCurPreviewBuffer, 0, data.length);
        }

        if (mIsNeedAutoFocus) {
            autoFocus();
            mIsNeedAutoFocus = false;
        }

        if (mPreviewSaver != null) {
            mPreviewSaver.saveBuffer(mContext, mRotateDegree, data);
        }

        if (mIsNeedAutoRecognize) {
            // 自动识别
            autoRecognize(data, camera);
        } else {
            if (camera != null) {
                camera.addCallbackBuffer(data);
            }
        }
    }

    /**
     * 自动识别
     */
    private void autoRecognize(final byte[] data, final Camera camera) {
        if (isCanExecute(mExecutor) && mIsFocusSuccess) {
            MXLog.i(TAG, "onAutoFocusMoving", "onRecognize");
            mExecutor.execute(new Runnable() {

                @Override
                public void run() {
                    if (isScanTimeOut()) {
                        dealScanTimeOut();
                        return;
                    } else {
//                        calculateFps();
                        onRecognize(data, mCardOrientationVertical);
                    }
                    if (camera != null) {
                        camera.addCallbackBuffer(data);
                    }
                }
            });
        } else {
            if (camera != null) {
                camera.addCallbackBuffer(data);
            }
        }
    }

    /**
     * 手动拍照
     */
    public Bitmap takePicture() {
        // 将mCurPreviewBuffer截图 并传递给ConfirmActivity
        if (mCurPreviewBuffer != null && mCamera != null) {
            byte[] scanByte = getScanByte(mCurPreviewBuffer);
            // 垂直方向时 不用调整Bitmap宽高（因为手机水平放置手机时没有拍照功能）
            Bitmap cropBitmap = getScanBitmap(scanByte, mClipRect.height(), mClipRect.width());
            return cropBitmap;

        }
        return null;
    }

    private void autoFocus() {
        MXLog.i(TAG, "onAutoFocus");
        if (mMXCameraProxy != null && !mIsFocusSuccess) {
            if (System.currentTimeMillis() - mLastFocusTime > 1000) {
                MXLog.i(TAG, "onAutoFocus", "111");
                mLastFocusTime = System.currentTimeMillis();
                mMXCameraProxy.autoFocus(new Camera.AutoFocusCallback() {
                    @Override
                    public void onAutoFocus(boolean success, Camera camera) {
                        MXLog.i(TAG, "onAutoFocus", "success", success);
                        mIsFocusSuccess = success;
                        if (!mIsFocusSuccess) {
                            mMainHandler.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    autoFocus();
                                }
                            }, 1000);
                        }
                    }
                });
            } else {
                mIsFocusSuccess = true;
            }
        }

    }

    /**
     * 扫描是否超时，
     *
     * @return
     */
    private boolean isScanTimeOut() {
        boolean isTimeOut = false;
        if (mScanTimeOut > 0 && (System.currentTimeMillis() - mStartDetectTime) > mScanTimeOut * 1000) {
            isTimeOut = true;
        }
        return isTimeOut;
    }

    private void dealScanTimeOut() {
        if (mCardScanListener != null) {
            mCardScanListener.scanTimeOut();
        }
    }

    //计算帧率相关变量
    private List<Long> mTimeCounter = new ArrayList<>();
    private int mTimeStart = 0;
    private int mFps;

    /**
     * 计算帧率
     */
    public void calculateFps() {
        int fps = 0;
        long timer = System.currentTimeMillis();
        mTimeCounter.add(timer);
        while (mTimeStart < mTimeCounter.size()
                && mTimeCounter.get(mTimeStart) < timer - 1000) {
            mTimeStart++;
        }
        mFps = mTimeCounter.size() - mTimeStart;
        if (mTimeStart > 100) {
            mTimeCounter = mTimeCounter.subList(mTimeStart,
                    mTimeCounter.size() - 1);
            mTimeStart = 0;
        }
        MXLog.i(TAG, "calculateFps", "mFps", mFps);
    }

    /**
     * 识别图片
     *
     * @param srcByte    扫描区域图片
     * @param isVertical 是否是垂直
     */
    protected void onRecognize(final byte[] srcByte, final boolean isVertical) {
        CardRecognizer cardRecognizer = getCardRecognizer();
        if (cardRecognizer != null) {
            final byte[] scanByte = getScanByte(srcByte);
            int reWidth = mClipRect.width() > mClipRect.height() ? mClipRect.width() : mClipRect.height();
            int reHeight = mClipRect.width() < mClipRect.height() ? mClipRect.width() : mClipRect.height();
            int recognizeWidth;
            int recognizeHeight;

            if (!isVertical) {
                // 测试 查看真正传入模型中的图片到底是什么样的
                recognizeWidth = reWidth;
                recognizeHeight = reHeight;
            } else {
                recognizeWidth = reHeight;
                recognizeHeight = reWidth;
            }

            // 测试 查看真正传入模型中的图片到底是什么样的
//            Bitmap cropBitmap = getScanBitmap(scanByte, recognizeWidth, recognizeHeight);
//            EventBus.getDefault().post(new NewFrameBmp(cropBitmap));

            final int finalRecognizeWidth = recognizeWidth;
            final int finalRecognizeHeight = recognizeHeight;
            cardRecognizer.recognizeCard(scanByte, recognizeWidth, recognizeHeight, null, isVertical, mIsInFrame,
                    new CardRecognizer.ICardRecognizeCallback() {
                        @Override
                        public void callback(com.moxie.ocr.ocr.base.Card card, Bitmap recognizeBitmap) {
                            if (card != null) {
                                Log.i("wk", "--->>> card.toString = "+card.toString());
                                if (mCardScanListener != null) {
                                    Bitmap cropBitmap = getScanBitmap(scanByte, finalRecognizeWidth, finalRecognizeHeight);
                                    mCardScanListener.onCardDetected(card, cropBitmap, recognizeBitmap);
//                                    mCardScanListener.onCardDetected(card, cropBitmap, cropBitmap);   // testCode
                                }
                            }
//                            else {
//                                Log.i("zhym", "--->>> card = "+card+", 未能识别！");
//                            }
                        }
                    });
        }
    }

    public byte[] getScanByte(byte[] srcByte) {
        if (mCardScanRect != null) {
            // 扩大扫描边框
            Rect realRect = Util.extendRect(mCardScanRect, getPreviewWidth(), getPreviewHeight(), mRotateDegree, mCardOrientationVertical);
            if (mRotateDegree == 0) {
                //横向左
                mClipRect = new Rect(realRect.left, realRect.top, realRect.right, realRect.bottom);
            } else if (mRotateDegree == 180) {
                //横向右
                mClipRect = new Rect(realRect.left, getPreviewHeight() - realRect.bottom, realRect.right, getPreviewHeight() - realRect.top);
            } else if (mRotateDegree == 90 || mRotateDegree == 270) {
                mClipRect = new Rect(realRect.top, realRect.left, realRect.bottom, realRect.right);
            }
        }

        // clipByte
        CardRecognizer cardRecognizer = getCardRecognizer();
        byte[] clipByte = cardRecognizer.clipNv21Byte(srcByte, mClipRect, getPreviewWidth(), getPreviewHeight());
        // scanByte
        return getPreviewScanByte(mRotateDegree, clipByte, mClipRect.width(), mClipRect.height());

    }

    private int[] getPreviewScanSize(int rotateDegree) {
        int[] scanSize = {getPreviewWidth(), getPreviewHeight()};
        if (rotateDegree == 90) {
            scanSize[0] = getPreviewHeight();
            scanSize[1] = getPreviewWidth();
        } else if (rotateDegree == 180) {
        } else if (rotateDegree == 270) {
            scanSize[0] = getPreviewHeight();
            scanSize[1] = getPreviewWidth();
        }
        return scanSize;
    }

    private byte[] getPreviewScanByte(int rotateDegree, byte[] srcData, int width, int height) {
        byte[] scanData = srcData;
        if (rotateDegree == 90) {
            scanData = MXImageUtils.rotateYUV420Degree90(srcData, width, height);
        } else if (rotateDegree == 180) {
            scanData = MXImageUtils.rotateYUV420Degree180(srcData, width, height);
        } else if (rotateDegree == 270) {
            scanData = MXImageUtils.rotateYUV420Degree270(srcData, width, height);
        }
        return scanData;
    }

    /**
     * 当前线程池是否可执行
     *
     * @param executor
     */
    private boolean isCanExecute(ExecutorService executor) {
        return executor != null && !executor.isShutdown();
    }

    /**
     * 根据相机返回的预览数据流获取进行识别的图像
     *
     * @param data
     * @param width
     * @param height
     * @return
     */
    private Bitmap getScanBitmap(byte[] data, int width, int height) {
        Bitmap scanBitmap = Util.NV21ToRGBABitmap(data, width, height, mContext);
        return scanBitmap;
    }

    /**
     * 设置OCR扫描回调
     *
     * @param cardScanListener
     */
    public void setCardScanListener(MXCardScanListener cardScanListener) {
        this.mCardScanListener = cardScanListener;
    }

    /**
     * 设置当前activity的旋转角度
     *
     * @param rotation
     */
    public void setRotation(int rotation) {
        this.mRotation = rotation;
        if (mMXCameraProxy != null) {
            mMXCameraProxy.setRotation(rotation);
        }
    }

    /**
     * 设置识别的方向
     *
     * @param cardOrientationVertical
     */
    protected void setCardOrientationVertical(boolean cardOrientationVertical) {
        mCardOrientationVertical = cardOrientationVertical;
    }

    /**
     * 获取预览的宽
     *
     * @return
     */
    public int getPreviewWidth() {
        return mMXCameraProxy != null ? mMXCameraProxy.getPreviewWidth() : 1280;
    }

    /**
     * 获取预览的高
     *
     * @return
     */
    public int getPreviewHeight() {
        return mMXCameraProxy != null ? mMXCameraProxy.getPreviewHeight() : 960;
    }

    /**
     * 设置扫描区域大小
     *
     * @param cardScanRect
     */
    public void setCardScanRect(Rect cardScanRect) {
        this.mCardScanRect = new Rect(cardScanRect);
        mCardScanRect.left &= ~1;
        mCardScanRect.right &= ~1;
        mCardScanRect.top &= ~1;
        mCardScanRect.bottom &= ~1;
    }

    protected abstract CardRecognizer initRecognizer(Context context) throws RecognizerInitFailException;

    public void setPreviewInfo(int previewTimeGaps, String previewStoragePath, int savedNum) {
        if (previewTimeGaps > 0 && previewStoragePath != null) {
            mPreviewSaver = new PreviewSaver(previewTimeGaps, previewStoragePath, savedNum);
            mPreviewSaver.setPreviewSize(getPreviewWidth(), getPreviewHeight());
        } else {
            mPreviewSaver = null;
        }
    }

    public void switchCameraFlashLight(boolean isStartFlash) {
        if (mMXCameraProxy != null && !mIsCameraRelease) {
            mMXCameraProxy.switchCameraFlashLight(isStartFlash);
        }
    }

    public void setIsInFrame(boolean isInFrame) {
        this.mIsInFrame = isInFrame;
    }

    public void setScanTimeOut(int scanTimeOut) {
        this.mScanTimeOut = scanTimeOut;
    }

    public void setTvDebug(TextView tvDebug) {
        this.mTvDebug = tvDebug;
    }

    public void setIsStartAutoFocus(boolean startAutoFocus) {
        this.mIsStartAutoFocus = startAutoFocus;
    }

    @Override
    public void onFocus() {
        MXLog.i(TAG, "accelerometer", "onFocus");
        mIsFocusSuccess = false;
        autoFocus();
    }
}

